package com.sinovatech.ologger.core.web.jsonplugin;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
import java.util.zip.GZIPOutputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *  Wrapper for JSONWriter with some utility methods.
 */
public class JSONUtil {
	final static String RFC3339_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
	private static final Log log = LogFactory.getLog(JSONUtil.class);

	/**
	 * Serializes an object into JSON.
	 * @param object to be serialized
	 * @return JSON string
	 * @throws JSONException
	 */
	public static String serialize(Object object) throws JSONException {
		JSONWriter writer = new JSONWriter();

		return writer.write(object);
	}

	/**
	 * Serializes an object into JSON, excluding any properties matching
	 * any of the regular expressions in the given collection.
	 * @param object to be serialized
	 * @param excludeProperties Patterns matching properties to exclude
	 * @param ignoreHierarchy whether to ignore properties defined on base classes of the root object
	 * @return JSON string
	 * @throws JSONException
	 */
	public static String serialize(Object object, Collection<Pattern> excludeProperties,
			Collection<Pattern> includeProperties, boolean ignoreHierarchy, boolean excludeNullProperties)
			throws JSONException {
		JSONWriter writer = new JSONWriter();
		writer.setIgnoreHierarchy(ignoreHierarchy);
		return writer.write(object, excludeProperties, includeProperties, excludeNullProperties);
	}

	/**
	 * Serializes an object into JSON, excluding any properties matching
	 * any of the regular expressions in the given collection.
	 * @param object to be serialized
	 * @param excludeProperties Patterns matching properties to exclude
	 * @param ignoreHierarchy whether to ignore properties defined on base classes of the root object
	 * @param enumAsBean whether to serialized enums a Bean or name=value pair
	 * @return JSON string
	 * @throws JSONException
	 */
	public static String serialize(Object object, Collection<Pattern> excludeProperties,
			Collection<Pattern> includeProperties, boolean ignoreHierarchy, boolean enumAsBean,
			boolean excludeNullProperties, int maxFetchLevel) throws JSONException {
		JSONWriter writer = new JSONWriter();
		writer.setIgnoreHierarchy(ignoreHierarchy);
		writer.setEnumAsBean(enumAsBean);
		writer.setMaxFetchLevel(maxFetchLevel);
		return writer.write(object, excludeProperties, includeProperties, excludeNullProperties);
	}

	/**
	 * Serializes an object into JSON to the given writer.
	 * @param writer Writer to serialize the object to
	 * @param object object to be serialized
	 * @throws IOException
	 * @throws JSONException
	 */
	public static void serialize(Writer writer, Object object) throws IOException, JSONException {
		writer.write(serialize(object));
	}

	/**
	 * Serializes an object into JSON to the given writer, excluding any properties matching
	 * any of the regular expressions in the given collection.
	 * @param writer Writer to serialize the object to
	 * @param object object to be serialized
	 * @param excludeProperties Patterns matching properties to ignore
	 * @throws IOException
	 * @throws JSONException
	 */
	public static void serialize(Writer writer, Object object, Collection<Pattern> excludeProperties,
			Collection<Pattern> includeProperties, boolean excludeNullProperties) throws IOException, JSONException {
		writer.write(serialize(object, excludeProperties, includeProperties, true, excludeNullProperties));
	}

	/**
	 * Deserializes a object from JSON
	 * @param json string in JSON
	 * @return desrialized object
	 * @throws JSONException
	 */
	public static Object deserialize(String json) throws JSONException {
		JSONReader reader = new JSONReader();
		return reader.read(json);
	}

	/**
	 * Deserializes a object from JSON
	 * @param reader Reader to read a JSON string from
	 * @return deserialized object
	 * @throws JSONException when IOException happens
	 */
	public static Object deserialize(Reader reader) throws JSONException {
		//read content
		BufferedReader bufferReader = new BufferedReader(reader);
		String line = null;
		StringBuilder buffer = new StringBuilder();

		try {
			while ((line = bufferReader.readLine()) != null) {
				buffer.append(line);
			}
		} catch (IOException e) {
			throw new JSONException(e);
		}

		return deserialize(buffer.toString());
	}

	public static void writeJSONToResponse(HttpServletResponse response, String encoding, boolean wrapWithComments,
			String serializedJSON, boolean smd, boolean gzip, boolean noCache) throws IOException {
		String json = serializedJSON == null ? "" : serializedJSON;
		if (wrapWithComments) {
			StringBuilder sb = new StringBuilder("/* ");
			sb.append(json);
			sb.append(" */");
			json = sb.toString();
		}
		if (log.isDebugEnabled()) {
			log.debug("[JSON]" + json);
		}

		response.setContentType((smd ? "application/json-rpc;charset=" : "application/json;charset=") + encoding);

		if (noCache) {
			response.setHeader("Cache-Control", "no-cache");
			response.setHeader("Expires", "0");
			response.setHeader("Pragma", "No-cache");
		}

		if (gzip) {
			response.addHeader("Content-Encoding", "gzip");
			GZIPOutputStream out = null;
			InputStream in = null;
			try {
				out = new GZIPOutputStream(response.getOutputStream());
				in = new ByteArrayInputStream(json.getBytes());
				byte[] buf = new byte[1024];
				int len;
				while ((len = in.read(buf)) > 0) {
					out.write(buf, 0, len);
				}
			} finally {
				if (in != null) {
					in.close();
				}
				if (out != null) {
					out.finish();
					out.close();
				}
			}

		} else {
			response.setContentLength(json.getBytes(encoding).length);
			PrintWriter out = response.getWriter();
			out.print(json);
		}
	}

	public static List<String> asList(String commaDelim) {
		if ((commaDelim == null) || (commaDelim.trim().length() == 0))
			return null;
		List<String> list = new ArrayList<String>();
		String[] split = commaDelim.split(",");
		for (String element : split) {
			String trimmed = element.trim();
			if (trimmed.length() > 0) {
				list.add(trimmed);
			}
		}
		return list;
	}

	public static boolean isGzipInRequest(HttpServletRequest request) {
		String header = request.getHeader("Accept-Encoding");
		return header != null && header.indexOf("gzip") >= 0;
	}
}
